package edu.scsu.csci.controller;

import java.lang.annotation.Annotation;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import edu.scsu.csci.IConverter;
import edu.scsu.csci.IExecutionErrors;
import edu.scsu.csci.IValidator;
import edu.scsu.csci.IView;
import edu.scsu.csci.annotation.AExpose;
import edu.scsu.csci.annotation.AView;
import edu.scsu.csci.context.ActionContext;
import edu.scsu.csci.context.ViewContext;
import edu.scsu.csci.converter.ConversionException;
import edu.scsu.csci.converter.ConvertorRegistry;
import edu.scsu.csci.core.ActionMethod;
import edu.scsu.csci.core.DOField;
import edu.scsu.csci.core.EScope;
import edu.scsu.csci.core.ExecutionError;
import edu.scsu.csci.core.ExecutionErrors;
import edu.scsu.csci.core.IRegistry;
import edu.scsu.csci.interceptor.EInterceptorFlow;
import edu.scsu.csci.util.ReflectionUtil;
import edu.scsu.csci.validator.TypeAnnotationRegistry;
import edu.scsu.csci.validator.ValidationException;
import edu.scsu.csci.validator.ValidatorAnnotationRegistory;

/**
 * The default implementation of the interface defined by the contorller class.
 * 
 * @author Maqbool Khan
 * @since jdk 1.5
 * @see
 * @viz.diagram DefaultController.tpx
 */
public class DefaultController extends AbstractController	{
	
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.IController#intialize(javax.servlet.ServletContext)
	 */
	public void intialize(ServletContext servletContext) {
		super.intialize(servletContext);
	}
	
	/**
	 * <p>	
	 * 		Populates and validates the domain object. If either a conversion or validation error occurs
	 * 		a null domain object is returned.
	 * </p>
	 * 
	 * @see edu.scsu.csci.controller.AbstractController
	 * 				#populateAndValidateDomainObject(java.lang.Class, javax.servlet.ServletRequest)
	 */
	@Override
	protected Object populateAndValidateDomainObject(Class<?> domainObjectClazz, ServletRequest request) {
				
		Object domainObject = ReflectionUtil.instantiate(domainObjectClazz);
		Enumeration<String> names = request.getParameterNames();
		DOField doField = null;
		String fieldName = null;
		while (names.hasMoreElements())	{
			try
			{
				fieldName = names.nextElement();
				doField = ReflectionUtil.getDOField(domainObject, fieldName);
				 
				//convert the field to the destination type
				IConverter covertor = ConvertorRegistry.getInstance().get(doField.getType());
				Object fieldValue = covertor.convert(request.getParameter(fieldName), doField.getType());
				
				//vaildate the field based on the validator defined
				IRegistry<Class<?>, Class<? extends Annotation>> typeAnnotationRegistry = TypeAnnotationRegistry.getInstance();
				Class<? extends Annotation> annotationClass = typeAnnotationRegistry.get(doField.getType());
				IRegistry<Class<? extends Annotation>, IValidator> validatorRegistory = ValidatorAnnotationRegistory.getInstance();
				Annotation validatorAnnotation = doField.getAnnotation(annotationClass);
				if(validatorAnnotation != null){
					validatorRegistory.get(annotationClass).validate(validatorAnnotation, fieldValue);
				}
				
				//if there are no errors so far set the field value
				doField.set(fieldValue);
			}
			catch(ConversionException ce){
				WebUtils.setExecutionError(fieldName, ce.getLocalizedMessage());
				return null;
			}
			catch(ValidationException ve){
				WebUtils.setExecutionError(fieldName, ve.getLocalizedMessage());
				return null;
			}
		}
		return domainObject;
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#performBuisnessValidation(java.lang.Object)
	 */
	@Override
	protected boolean performBuisnessValidation(Object domainObject, ActionContext actionContext) {
		String validationMethodName = actionContext.getValidationMethod();
		if(validationMethodName == null || "".equalsIgnoreCase(validationMethodName)){
			return true;
		}
		return (Boolean) ReflectionUtil.invoke(actionContext.getServiceObject(), validationMethodName, domainObject);
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#performBuisnessValidation(java.lang.Object)
	 */
	@Override
	protected void setErrors(ActionContext actionContext){
		actionContext.getServletRequest().setAttribute("errors", WebUtils.getExecutionErrors());		
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#invokeInterceptors(edu.scsu.csci.context.ActionContext)
	 */
	@Override
	protected EInterceptorFlow preProcessInterceptors(Object domainObject, ServletRequest request, 
				ServletResponse response, ActionContext operationContext){
		return operationContext.getInterceptors().preProcess(domainObject, request, response);
	}
	
	@Override
	protected EInterceptorFlow postProcessInterceptors(Object domainObject, ServletRequest request, 
				ServletResponse response, ActionContext operationContext){
		return operationContext.getInterceptors().postProcess(domainObject, request, response);
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#invokeAction()
	 */
	@Override
	protected Object invokeAction(Object businessObject, Object domainObject, ActionMethod commandMethod) {
		return commandMethod.invoke(businessObject, domainObject);
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#setDomainObject(edu.scsu.csci.context.ActionContext, java.lang.Object)
	 */
	protected void setObjects(ActionContext actionContext, Object domainObject, Object returnedObject){
		EScope scope = actionContext.getDomainObjectScope();
		setObject(actionContext, domainObject, scope, actionContext.getDomainObjectKey());
		scope = actionContext.getReturnedObjectScope();
		setObject(actionContext, returnedObject, scope, actionContext.getReturnObjectKey()); 
	}
	
	private void setObject(ActionContext actionContext, Object object, EScope scope, String key){
		if(object == null|| actionContext == null){
			return;
		}
		
		if(key == null || "".equals(key)){
			key = object.getClass().getName();
			key = key.substring(key.lastIndexOf('.') + 1);
		}
		
		switch(scope){
			case NONE: break;
			case REQUEST: 
				ServletRequest request = actionContext.getServletRequest();
				request.setAttribute(key, object);
				break;
			case SESSION: 
				HttpSession session = ((HttpServletRequest) actionContext.getServletRequest()).getSession(true); 
				if(session != null){
					session.setAttribute(key, object);
				}
				break;
			case APPLICATION:  
				this.servletContext.setAttribute(key, object);
				break;
				
		}
	}
	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.controller.AbstractController#delegateToView()
	 */
	@Override
	protected void delegateToView(ServletRequest request, ServletResponse response, ViewContext viewContext) {
		IView viewStratergy = viewContext.getViewStratergy();
		viewStratergy.render(viewContext);
	}

	@Override
	protected boolean isActionInvocationAllowed(Object businessObject, ActionMethod actionMethod) {
		AExpose aExpose = businessObject.getClass().getAnnotation(AExpose.class);
		if(!aExpose.expose()){
			return false;
		}
		String[] disAllowedMethods =  aExpose.disAllow();
					
		for(int i = 0, length = disAllowedMethods.length; i < length; i++){
			if(actionMethod.getName().equalsIgnoreCase(disAllowedMethods[i]))	{
				return false;
			}
		}
		return true;
	}
}
